/**
 * 本质上就是二维平面给N个点，要求一个D*W的矩形区域，使得里面的点最多
 * 将所有点按X坐标排序, 设置宽度为D的滑动窗口
 * 根据滑动窗口的移动，增加或者删除点
 * 对每一个增加的点，其Y坐标，对[Y-W+1, Y]区间有贡献，贡献为加1
 * 因此这是一个线段树成段更新的问题，答案就是线段树中的最大值
 * 对每一个删除的点，只需成段减一即可
 * 注意线段树的范围不是N，而是max(Y)
 */
#include <bits/stdc++.h>
using namespace std;

#ifndef ONLINE_JUDGE
int const SZ = 5;
#else
int const SZ = 200001;
#endif

using llt = long long;

struct SegTree{ // 线段树带延迟

using llt = long long;

using value_type = llt;
vector<value_type> data; // 线段树

using lazy_type = llt;
vector<lazy_type> lazy; // 延迟标记

/// 从下往上计算信息，要变动
value_type _up_(const value_type & ls, const value_type & rs) {
    return max(ls, rs);
}

/// 从上往下计算信息，要变动
void _dn_(int t, int s, int e, const lazy_type & delta) {
    auto n = e - s + 1;
    data[t] += delta;
    lazy[t] += delta;
}

/// 初始化，清零，不用动
void init(int n) {
    data.assign(n + 1 << 2, value_zero());
    lazy.assign(n + 1 << 2, lazy_zero());
}

/// 这个函数不用动
void build(int n) {
    _build(1, 1, n);
}

/// 几乎不用动
value_type query(int t, int s, int e, int a, int b) {
    if(a <= s and e <= b) {
        return data[t];
    }
    _pushDown(t, s, e);
    int mid = (s + e) >> 1;
    value_type ans = value_zero();
    if(a <= mid) ans = _up_(ans, query(lson(t), s, mid, a, b));
    if(mid < b) ans = _up_(ans, query(rson(t), mid + 1, e, a, b));
    return ans;
}

/// 几乎不用动
void modify(int t, int s, int e, int a, int b, const lazy_type & delta) {
    if(a <= s and e <= b) {
        _dn_(t, s, e, delta);
        return;
    }
    _pushDown(t, s, e);
    int mid = (s + e) >> 1;
    if(a <= mid) modify(lson(t), s, mid, a, b, delta);
    if(mid < b) modify(rson(t), mid + 1, e, a, b, delta);
    _pushUp(t);
    return;
}

/// 这个函数不用动
void _pushUp(int t) {
    data[t] = _up_(data[lson(t)], data[rson(t)]);
}

/// 这个函数几乎不用动
void _pushDown(int t, int s, int e) {
    if(lazy_zero() == lazy[t]) return;
    auto & lz = lazy[t];
    auto ls = lson(t), rs = rson(t);
    int mid = (s + e) >> 1;

    _dn_(ls, s, mid, lz);
    _dn_(rs, mid + 1, e, lz);

    lz = lazy_zero();
}


/// 几乎不用动
void _build(int t, int s, int e) {
    if(s == e) {
        int x; cin >> x;
        data[t] = value_type(x); // 注意提供value_type的初始化
        return; 
    }
    int mid = (s + e) >> 1;
    _build(lson(t), s, mid);
    _build(rson(t), mid + 1, e);
    _pushUp(t);
}

/// 辅助函数，视延迟的类型而变动
static const lazy_type & lazy_zero() {
    static const lazy_type LAZY0 = 0;
    return LAZY0; 
}

/// 辅助函数，视线段树信息类型而变动
static const value_type & value_zero() {
    static const value_type VALUE0 = 0;
    return VALUE0;
}

/// 这两个函数不用变动
static int lson(int x) {return x << 1;}
static int rson(int x) {return lson(x) | 1;}


}St;

int N, D, W;
vector<pair<int, int>> A;

llt proc(){
    sort(A.begin(), A.end());
    St.init(SZ);

    llt ans = 0;
    int pre = 0, cur = 0;
    while(1){
        while(cur < N and A[cur].first - A[pre].first < D){
            St.modify(1, 1, SZ, max(1, A[cur].second-W+1), A[cur].second, 1);
            ++cur;
            ans = max(ans, St.query(1, 1, SZ, 1, SZ));
        } 
        if(cur == N) break;

        St.modify(1, 1, SZ, max(1, A[pre].second-W+1), A[pre].second, -1);
        ++pre;
    }

    return ans;
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("z.txt", "r", stdin);
#endif
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    cin >> N >> D >> W;
    A.assign(N, {});
    for(auto & p : A) cin >> p.first >> p.second;
    cout << proc() << endl;
    return 0;
}